package org.abc.fund.service.strategy;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import org.abc.fund.controller.strategy.response.AdjustmentDetailResponse;
import org.abc.fund.entity.funds.FundBasicInfo;
import org.abc.fund.entity.funds.FundNetValueHistory;
import org.abc.fund.entity.strategy.*;
import org.abc.fund.entity.strategy.Backtest;
import org.abc.fund.entity.strategy.CombinationStrategy;
import org.abc.fund.entity.strategy.StrategyAdjustmentHistory;
import org.abc.fund.repository.funds.FundBasicInfoRepository;
import org.abc.fund.repository.funds.FundNetValueHistoryRepository;
import org.abc.fund.repository.strategy.BacktestRepository;
import org.abc.fund.repository.strategy.CombinationStrategyRepository;
import org.abc.fund.repository.strategy.StrategyAdjustmentHistoryRepository;
import org.abc.fund.repository.strategy.StrategyProfitHistoryRepository;
import org.abc.fund.service.strategy.request.backtest.BacktestRequest;
import org.abc.fund.service.strategy.request.create_strategy.CreateStrategyRequest;
import org.abc.fund.service.strategy.request.create_strategy.StrategyComponentRequest;
import org.abc.fund.service.strategy.request.manual_adjustment.ManualAdjustmentRequest;
import org.abc.fund.service.strategy.request.reject_strategy.RejectStrategyRequest;
import org.abc.fund.util.exception.ResourceNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CombinationStrategyService {
  // 以下为自动构造函数注入的仓库依赖
  private final FundBasicInfoRepository fundBasicInfoRepository;
  private final CombinationStrategyRepository combinationStrategyRepository;
  private final StrategyAdjustmentHistoryRepository strategyAdjustmentHistoryRepository;
  private final FundNetValueHistoryRepository fundNetValueHistoryRepository;
  private final BacktestRepository backtestRepository;
  private final StrategyProfitHistoryRepository strategyProfitHistoryRepository;

  // 构造函数，用于注入所需的仓库
  public CombinationStrategyService(
      FundBasicInfoRepository fundBasicInfoRepository,
      CombinationStrategyRepository combinationStrategyRepository,
      StrategyAdjustmentHistoryRepository strategyAdjustmentHistoryRepository,
      FundNetValueHistoryRepository fundNetValueHistoryRepository,
      BacktestRepository backtestRepository,
      StrategyProfitHistoryRepository strategyProfitHistoryRepository) {
    this.fundBasicInfoRepository = fundBasicInfoRepository;
    this.combinationStrategyRepository = combinationStrategyRepository;
    this.strategyAdjustmentHistoryRepository = strategyAdjustmentHistoryRepository;
    this.fundNetValueHistoryRepository = fundNetValueHistoryRepository;
    this.backtestRepository = backtestRepository;
    this.strategyProfitHistoryRepository = strategyProfitHistoryRepository;
  }

  /**
   * 创建一个新的组合策略
   *
   * @param request 包含策略创建信息的请求对象
   * @return 保存后的组合策略对象
   */
  @Transactional
  public CombinationStrategy createStrategy(CreateStrategyRequest request) {
    return updateStrategy(null, request);
  }

  /**
   * 更新组合策略
   *
   * @param strategyId 组合策略的ID
   * @param request 策略更新信息
   * @return 更新后的策略对象
   */
  @Transactional
  public CombinationStrategy updateStrategy(Long strategyId, CreateStrategyRequest request) {
    CombinationStrategy strategy;

    // 判断策略是否存在
    if (strategyId != null) {
      strategy =
          combinationStrategyRepository
              .findById(strategyId)
              .orElseThrow(
                  () -> new ResourceNotFoundException("Strategy not found with ID: " + strategyId));
    } else {
      strategy = CombinationStrategy.builder().build();
    }

    // 初始化策略对象
    strategy.setName(request.getName());
    strategy.setType(request.getType());
    strategy.setDescription(request.getDescription());
    strategy.setStatus(CombinationStrategy.Status.DRAFT);
    strategy.setTargetAudience(request.getTargetAudience());
    strategy.setRiskLevel(request.getRiskLevel());
    strategy.setBenchmark(request.getBenchmark());

    // 处理成分基金
    if (request.getComponents() != null && !request.getComponents().isEmpty()) {
      List<StrategyComponent> funds =
          request.getComponents().stream()
              .map(
                  dto -> {
                    FundBasicInfo fundInfo =
                        fundBasicInfoRepository
                            .findById(dto.getFundCode())
                            .orElseThrow(
                                () ->
                                    new IllegalArgumentException(
                                        "Invalid fund code provided: " + dto.getFundCode()));
                    return StrategyComponent.builder()
                        .fund(fundInfo)
                        .weight(dto.getWeight())
                        .build();
                  })
              .collect(Collectors.toList());
      strategy.setComponents(funds);
    }

    // 处理再平衡规则
    if (request.getRebalanceRule() != null) {
      RebalanceRule rebalanceRule =
          RebalanceRule.builder()
              .type(request.getRebalanceRule().getType())
              .frequency(request.getRebalanceRule().getFrequency())
              .threshold(request.getRebalanceRule().getThreshold())
              .build();
      strategy.setRebalanceRule(rebalanceRule);
    }

    // 保存并返回策略对象
    return combinationStrategyRepository.save(strategy);
  }

  /**
   * 获取所有组合策略
   *
   * @return 所有组合策略的列表
   */
  @Transactional(readOnly = true)
  public List<CombinationStrategy> getAllStrategies() {
    return combinationStrategyRepository.findAll();
  }

  /**
   * 根据ID获取组合策略
   *
   * @param id 组合策略的ID
   * @return 可能包含组合策略对象的Optional
   */
  @Transactional(readOnly = true)
  public Optional<CombinationStrategy> getStrategyById(Long id) {
    return combinationStrategyRepository.findById(id);
  }

  /**
   * 更新组合策略的状态
   *
   * @param strategyId 组合策略的ID
   * @param newStatus 新的状态
   * @return 更新后的组合策略对象
   */
  @Transactional
  public CombinationStrategy updateStrategyStatus(
      Long strategyId, CombinationStrategy.Status newStatus) {
    CombinationStrategy strategy =
        combinationStrategyRepository
            .findById(strategyId)
            .orElseThrow(
                () -> new ResourceNotFoundException("Strategy not found with id: " + strategyId));
    strategy.setStatus(newStatus);
    return combinationStrategyRepository.save(strategy);
  }

  /**
   * 执行组合策略的回测
   *
   * @param strategyId 组合策略的ID
   * @param request 回测请求对象
   * @return 保存后的回测对象
   */
  @Transactional
  public Backtest runBacktest(Long strategyId, BacktestRequest request) {
    CombinationStrategy strategy =
        combinationStrategyRepository
            .findById(strategyId)
            .orElseThrow(
                () -> new ResourceNotFoundException("Strategy not found with id: " + strategyId));

    // 计算回测的最终价值
    BigDecimal finalValue = request.getInitialBalance();
    for (StrategyComponent fund : strategy.getComponents()) {
      var history =
          fundNetValueHistoryRepository.findByFundCodeAndNetDateBetweenOrderByNetDateDesc(
              fund.getFund().getCode(), request.getStartDate(), request.getEndDate());
      if (history.size() < 2) continue;

      BigDecimal startValue = history.getLast().getAccumulatedNav(); // getLast() 获取期初净值
      BigDecimal endValue = history.getFirst().getAccumulatedNav(); // getFirst() 获取期末净值

      if (startValue == null || endValue == null || startValue.compareTo(BigDecimal.ZERO) == 0)
        continue;

      BigDecimal returnRate =
          endValue.divide(startValue, 4, RoundingMode.HALF_UP).subtract(BigDecimal.ONE);
      BigDecimal fundAllocation =
          request
              .getInitialBalance()
              .multiply(fund.getWeight())
              .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
      BigDecimal fundEndValue = fundAllocation.multiply(BigDecimal.ONE.add(returnRate));
      finalValue = finalValue.add(fundEndValue.subtract(fundAllocation));
    }

    // 计算总回报和总回报率
    BigDecimal totalReturn = finalValue.subtract(request.getInitialBalance());
    BigDecimal totalReturnRate =
        totalReturn
            .divide(request.getInitialBalance(), 4, RoundingMode.HALF_UP)
            .multiply(new BigDecimal("100"));

    // 构建回测结果的JSON字符串
    String resultJson =
        String.format(
            "{\"initialBalance\": %.2f, \"finalValue\": %.2f, \"totalReturn\": %.2f, \"totalReturnRate\": %.2f}",
            request.getInitialBalance(), finalValue, totalReturn, totalReturnRate);

    // 创建并保存回测对象
    Backtest backtest =
        Backtest.builder()
            .strategy(strategy)
            .startDate(request.getStartDate())
            .endDate(request.getEndDate())
            .initialBalance(request.getInitialBalance())
            .result(resultJson)
            .build();
    return backtestRepository.save(backtest);
  }

  /**
   * 执行组合策略的手动调仓
   *
   * @param strategyId 组合策略的ID
   * @param request 手动调仓请求对象
   * @return 保存后的调仓历史对象
   */
  @Transactional
  public StrategyAdjustmentHistory performManualAdjustment(
      Long strategyId, ManualAdjustmentRequest request) {
    CombinationStrategy strategy =
        combinationStrategyRepository
            .findById(strategyId)
            .orElseThrow(
                () -> new ResourceNotFoundException("Strategy not found with id: " + strategyId));

    // 更新/添加
    List<AdjustmentDetail> details =
        request.getDetails().stream()
            .map(
                newComponent -> {
                  StrategyComponent oldComponent = null;
                  for (StrategyComponent component : strategy.getComponents()) {
                    if (component.getFund().getCode().equals(newComponent.getFundCode())) {
                      oldComponent = component;
                      break;
                    }
                  }

                  AdjustmentDetail detail;
                  if (oldComponent != null) {
                    detail =
                        AdjustmentDetail.builder()
                            .fundCode(oldComponent.getFund().getCode())
                            .fromWeight(oldComponent.getWeight())
                            .toWeight(newComponent.getWeight())
                            .build();
                    oldComponent.setWeight(newComponent.getWeight());
                  } else {
                    detail =
                        AdjustmentDetail.builder()
                            .fundCode(newComponent.getFundCode())
                            .fromWeight(BigDecimal.ZERO)
                            .toWeight(newComponent.getWeight())
                            .build();

                    FundBasicInfo fund =
                        fundBasicInfoRepository
                            .findById(newComponent.getFundCode())
                            .orElseThrow(
                                () ->
                                    new IllegalArgumentException(
                                        "Invalid fund code: " + newComponent.getFundCode()));
                    strategy
                        .getComponents()
                        .add(
                            StrategyComponent.builder()
                                .fund(fund)
                                .weight(newComponent.getWeight())
                                .build());
                  }

                  return detail;
                })
            .collect(Collectors.toList());

    // 检查删除
    var removedComponents =
        strategy.getComponents().stream()
            .map(
                oldComponent -> {
                  for (StrategyComponentRequest newComponent : request.getDetails()) {
                    if (oldComponent.getFund().getCode().equals(newComponent.getFundCode()))
                      return null;
                  }
                  return oldComponent;
                })
            .filter(Objects::nonNull)
            .collect(Collectors.toList());

    // 执行删除
    strategy.getComponents().removeAll(removedComponents);

    // 记录删除
    details.addAll(
        removedComponents.stream()
            .map(
                component ->
                    AdjustmentDetail.builder()
                        .fundCode(component.getFund().getCode())
                        .fromWeight(component.getWeight())
                        .toWeight(BigDecimal.ZERO)
                        .build())
            .collect(Collectors.toList()));

    // 保存策略
    combinationStrategyRepository.save(strategy);

    // 保存调仓历史
    StrategyAdjustmentHistory history =
        StrategyAdjustmentHistory.builder()
            .strategy(strategy)
            .reason(request.getReason())
            .details(details)
            .build();

    return strategyAdjustmentHistoryRepository.save(history);
  }

  /**
   * 获取组合策略的调仓历史
   *
   * @param strategyId 组合策略的ID
   * @return 调仓历史的列表
   */
  @Transactional(readOnly = true)
  public List<StrategyAdjustmentHistory> getAdjustmentHistory(Long strategyId) {
    if (!combinationStrategyRepository.existsById(strategyId))
      throw new ResourceNotFoundException("Strategy not found with id: " + strategyId);
    return strategyAdjustmentHistoryRepository.findByStrategy_IdOrderByCreatedAtDescIdDesc(
        strategyId);
  }

  /**
   * 拒绝一个策略，并记录原因
   *
   * @param strategyId 要拒绝的策略 ID
   * @return 更新后的策略实体
   */
  @Transactional
  public CombinationStrategy rejectStrategy(Long strategyId, RejectStrategyRequest request) {
    // 校验拒绝原因不能为空
    String reason = request.getReason();

    // 查找策略，如果不存在则抛出异常
    CombinationStrategy strategy =
        combinationStrategyRepository
            .findById(strategyId)
            .orElseThrow(() -> new ResourceNotFoundException("未找到ID为 " + strategyId + " 的策略"));

    // 设置新状态和拒绝原因
    strategy.setStatus(CombinationStrategy.Status.REJECTED);
    strategy.setRejectionReason(reason);

    // 保存并返回更新后的策略
    return combinationStrategyRepository.save(strategy);
  }

  /**
   * 提交策略以供审核
   *
   * @param strategyId 要提交的策略 ID
   * @return 更新后的策略
   */
  @Transactional
  public CombinationStrategy submitForReview(Long strategyId) {
    CombinationStrategy strategy =
        combinationStrategyRepository
            .findById(strategyId)
            .orElseThrow(() -> new ResourceNotFoundException("未找到ID为 " + strategyId + " 的策略"));

    // 只有“草稿”或“已拒绝”状态的策略才能提交审核
    if (strategy.getStatus() != CombinationStrategy.Status.DRAFT
        && strategy.getStatus() != CombinationStrategy.Status.REJECTED) {
      throw new IllegalStateException("只有处于'草稿'或'已拒绝'状态的策略才能提交审核。");
    }

    strategy.setStatus(CombinationStrategy.Status.UNDER_REVIEW);
    strategy.setRejectionReason(null); // 提交审核时，清除上一次的拒绝理由
    return combinationStrategyRepository.save(strategy);
  }

  /**
   * 批准策略
   *
   * @param strategyId 要批准的策略 ID
   * @return 更新后的策略
   */
  @Transactional
  public CombinationStrategy approveStrategy(Long strategyId) {
    CombinationStrategy strategy =
        combinationStrategyRepository
            .findById(strategyId)
            .orElseThrow(() -> new ResourceNotFoundException("未找到ID为 " + strategyId + " 的策略"));

    // 只能批准“审核中”的策略
    if (strategy.getStatus() != CombinationStrategy.Status.UNDER_REVIEW) {
      throw new IllegalStateException("只有'UNDER_REVIEW'的策略才能被批准。");
    }

    strategy.setStatus(CombinationStrategy.Status.ACTIVE); // 批准后状态变为“已上架”
    return combinationStrategyRepository.save(strategy);
  }

  /**
   * 获取所有待审核的策略列表
   *
   * @return 待审核策略的列表
   */
  public List<CombinationStrategy> getStrategiesForReview() {
    return combinationStrategyRepository.findByStatus(CombinationStrategy.Status.UNDER_REVIEW);
  }

  /**
   * 获取今日回报率
   *
   * @param strategyId 策略ID
   * @return 今日回报率 (例如: 0.01 代表 1%)
   */
  @Transactional
  public BigDecimal getTodayReturn(Long strategyId) {
    LocalDate today = LocalDate.now();
    Optional<StrategyProfitHistory> history =
        strategyProfitHistoryRepository.findByStrategyIdAndRecordDate(strategyId, today);
    if (history.isPresent()) {
      return history.get().getDailyReturn();
    }
    return calculateAndSaveDailyReturn(strategyId, today);
  }

  /**
   * 获取累计回报率
   *
   * @param strategyId 策略ID
   * @return 累计回报率 (例如: 0.15 代表 15%)
   */
  @Transactional(readOnly = true)
  public BigDecimal getCumulativeReturn(Long strategyId) {
    return strategyProfitHistoryRepository
        .findTopByStrategyIdOrderByRecordDateDesc(strategyId)
        .map(StrategyProfitHistory::getCumulativeReturn)
        .orElse(BigDecimal.ZERO);
  }

  /**
   * 计算并保存每日回报率
   *
   * @param strategyId 策略ID
   * @param date 日期
   * @return 当日回报率
   */
  private BigDecimal calculateAndSaveDailyReturn(Long strategyId, LocalDate date) {
    CombinationStrategy strategy =
        combinationStrategyRepository
            .findById(strategyId)
            .orElseThrow(
                () -> new ResourceNotFoundException("Strategy not found with id: " + strategyId));

    BigDecimal dailyReturn = BigDecimal.ZERO;
    // 注意：这里仍然需要优化为获取上一个交易日
    LocalDate previousDay = date.minusDays(1);

    for (StrategyComponent component : strategy.getComponents()) {
      String fundCode = component.getFund().getCode();
      FundNetValueHistory todayNetValue =
          fundNetValueHistoryRepository
              .findByFundCodeAndNetDateBetweenOrderByNetDateDesc(fundCode, date, date)
              .stream()
              .findFirst()
              .orElse(null);
      FundNetValueHistory previousDayNetValue =
          fundNetValueHistoryRepository
              .findByFundCodeAndNetDateBetweenOrderByNetDateDesc(fundCode, previousDay, previousDay)
              .stream()
              .findFirst()
              .orElse(null);

      if (todayNetValue != null
          && previousDayNetValue != null
          && previousDayNetValue.getAccumulatedNav().compareTo(BigDecimal.ZERO) != 0) {
        BigDecimal returnRate =
            todayNetValue
                .getAccumulatedNav()
                .divide(previousDayNetValue.getAccumulatedNav(), 8, RoundingMode.HALF_UP)
                .subtract(BigDecimal.ONE);
        // 策略的当日回报率 = 成分基金回报率的加权平均
        dailyReturn =
            dailyReturn.add(
                component
                    .getWeight()
                    .divide(new BigDecimal("100"), 8, RoundingMode.HALF_UP)
                    .multiply(returnRate));
      }
    }

    // 获取昨天的累计回报率
    BigDecimal lastCumulativeReturn =
        strategyProfitHistoryRepository
            .findTopByStrategyIdOrderByRecordDateDesc(strategyId)
            .map(StrategyProfitHistory::getCumulativeReturn)
            .orElse(BigDecimal.ZERO);

    // 今天的累计回报率 = (1 + 昨天的累计回报率) * (1 + 今天的日回报率) - 1
    BigDecimal currentCumulativeReturn =
        (BigDecimal.ONE.add(lastCumulativeReturn))
            .multiply(BigDecimal.ONE.add(dailyReturn))
            .subtract(BigDecimal.ONE);

    StrategyProfitHistory profitHistory =
        StrategyProfitHistory.builder()
            .strategy(strategy)
            .recordDate(date)
            .dailyReturn(dailyReturn)
            .cumulativeReturn(currentCumulativeReturn)
            .build();

    strategyProfitHistoryRepository.save(profitHistory);
    return dailyReturn;
  }

  /**
   * 获取策略今日的成交额 (换手率)
   *
   * @param strategyId 策略ID
   * @return 今日的换手率 (百分比)
   */
  @Transactional(readOnly = true)
  public BigDecimal getTodayTurnoverRate(Long strategyId) {
    if (!combinationStrategyRepository.existsById(strategyId)) {
      throw new ResourceNotFoundException("Strategy not found with id: " + strategyId);
    }

    // 获取今天所有的调仓历史记录
    List<StrategyAdjustmentHistory> todayAdjustments =
        strategyAdjustmentHistoryRepository.findByStrategy_IdAndCreatedAt(
            strategyId, LocalDate.now());

    if (todayAdjustments.isEmpty()) {
      return BigDecimal.ZERO;
    }

    // 计算总的卖出权重
    return todayAdjustments.stream()
        .flatMap(history -> history.getDetails().stream()) // 将所有调仓明细合并到一个流中
        .map(
            detail -> {
              BigDecimal fromWeight = detail.getFromWeight();
              BigDecimal toWeight = detail.getToWeight();
              // 计算权重的减少量，如果是增加则为0
              if (fromWeight.compareTo(toWeight) > 0) {
                return fromWeight.subtract(toWeight);
              }
              return BigDecimal.ZERO;
            })
        .reduce(BigDecimal.ZERO, BigDecimal::add);
  }
}
